# -*- coding: utf-8 -*-
"""
Redis配置模块
实现Redis缓存连接管理

主要功能:
- Redis连接配置
- 连接池管理
- 缓存操作封装
- 健康检查
"""

import os
import json
import asyncio
from typing import Optional, Any, Dict, Union, List
import aioredis
from aioredis import Redis
from aioredis.exceptions import ConnectionError, TimeoutError, RedisError
import logging
from datetime import datetime, timezone, timedelta

# 配置日志
logger = logging.getLogger(__name__)


class RedisConfig:
    """Redis配置类"""
    
    def __init__(self):
        # 从环境变量获取配置
        self.redis_url = os.getenv('REDIS_URL', 'redis://localhost:6379')
        self.redis_host = os.getenv('REDIS_HOST', 'localhost')
        self.redis_port = int(os.getenv('REDIS_PORT', '6379'))
        self.redis_db = int(os.getenv('REDIS_DB', '0'))
        self.redis_password = os.getenv('REDIS_PASSWORD')
        self.redis_username = os.getenv('REDIS_USERNAME')
        
        # 连接池配置
        self.max_connections = int(os.getenv('REDIS_MAX_CONNECTIONS', '50'))
        self.retry_on_timeout = os.getenv('REDIS_RETRY_ON_TIMEOUT', 'True').lower() == 'true'
        self.socket_timeout = float(os.getenv('REDIS_SOCKET_TIMEOUT', '5.0'))
        self.socket_connect_timeout = float(os.getenv('REDIS_SOCKET_CONNECT_TIMEOUT', '5.0'))
        
        # 缓存配置
        self.default_ttl = int(os.getenv('REDIS_DEFAULT_TTL', '3600'))  # 默认1小时
        self.key_prefix = os.getenv('REDIS_KEY_PREFIX', 'ai_marketiq:')
        
        # 连接实例
        self._redis: Optional[Redis] = None
        self._is_connected = False
    
    async def connect(self) -> bool:
        """
        连接到Redis服务器
        
        Returns:
            bool: 连接是否成功
        """
        try:
            if self._redis is None:
                logger.info(f"正在连接到Redis: {self.redis_host}:{self.redis_port}")
                
                # 构建连接参数
                connection_kwargs = {
                    'host': self.redis_host,
                    'port': self.redis_port,
                    'db': self.redis_db,
                    'max_connections': self.max_connections,
                    'retry_on_timeout': self.retry_on_timeout,
                    'socket_timeout': self.socket_timeout,
                    'socket_connect_timeout': self.socket_connect_timeout,
                    'decode_responses': True,
                    'encoding': 'utf-8'
                }
                
                # 添加认证信息
                if self.redis_password:
                    connection_kwargs['password'] = self.redis_password
                if self.redis_username:
                    connection_kwargs['username'] = self.redis_username
                
                # 创建Redis连接
                self._redis = aioredis.from_url(
                    self.redis_url,
                    **connection_kwargs
                ) if self.redis_url.startswith('redis://') else aioredis.Redis(**connection_kwargs)
                
                # 测试连接
                await self._redis.ping()
                
                self._is_connected = True
                logger.info(f"成功连接到Redis服务器: {self.redis_host}:{self.redis_port}")
                
                return True
                
        except (ConnectionError, TimeoutError) as e:
            logger.error(f"连接Redis失败: {e}")
            self._is_connected = False
            return False
        except Exception as e:
            logger.error(f"连接Redis时发生未知错误: {e}")
            self._is_connected = False
            return False
    
    async def disconnect(self):
        """
        断开Redis连接
        """
        if self._redis:
            logger.info("正在断开Redis连接")
            await self._redis.close()
            self._redis = None
            self._is_connected = False
            logger.info("已断开Redis连接")
    
    async def health_check(self) -> Dict[str, Any]:
        """
        Redis健康检查
        
        Returns:
            Dict: 健康检查结果
        """
        health_info = {
            'status': 'unhealthy',
            'server': f"{self.redis_host}:{self.redis_port}",
            'timestamp': datetime.now(timezone.utc).isoformat(),
            'details': {}
        }
        
        try:
            if not self._redis or not self._is_connected:
                health_info['details']['error'] = 'Redis未连接'
                return health_info
            
            # 执行ping命令
            start_time = asyncio.get_event_loop().time()
            pong = await self._redis.ping()
            ping_time = (asyncio.get_event_loop().time() - start_time) * 1000
            
            # 获取Redis信息
            info = await self._redis.info()
            
            health_info.update({
                'status': 'healthy',
                'details': {
                    'ping_response': pong,
                    'ping_time_ms': round(ping_time, 2),
                    'redis_version': info.get('redis_version'),
                    'used_memory_human': info.get('used_memory_human'),
                    'connected_clients': info.get('connected_clients'),
                    'total_commands_processed': info.get('total_commands_processed'),
                    'keyspace_hits': info.get('keyspace_hits'),
                    'keyspace_misses': info.get('keyspace_misses'),
                    'uptime_in_seconds': info.get('uptime_in_seconds')
                }
            })
            
            # 计算命中率
            hits = info.get('keyspace_hits', 0)
            misses = info.get('keyspace_misses', 0)
            if hits + misses > 0:
                hit_rate = hits / (hits + misses) * 100
                health_info['details']['hit_rate_percent'] = round(hit_rate, 2)
            
        except Exception as e:
            health_info['details']['error'] = str(e)
            logger.error(f"Redis健康检查失败: {e}")
        
        return health_info
    
    def _get_key(self, key: str) -> str:
        """
        获取带前缀的键名
        
        Args:
            key: 原始键名
            
        Returns:
            str: 带前缀的键名
        """
        return f"{self.key_prefix}{key}"
    
    async def set(self, key: str, value: Any, ttl: Optional[int] = None) -> bool:
        """
        设置缓存值
        
        Args:
            key: 键名
            value: 值
            ttl: 过期时间（秒），None表示使用默认TTL
            
        Returns:
            bool: 设置是否成功
        """
        try:
            if not self._redis:
                return False
            
            # 序列化值
            if isinstance(value, (dict, list)):
                value = json.dumps(value, ensure_ascii=False)
            elif not isinstance(value, str):
                value = str(value)
            
            # 设置过期时间
            expire_time = ttl if ttl is not None else self.default_ttl
            
            # 设置值
            result = await self._redis.setex(
                self._get_key(key),
                expire_time,
                value
            )
            
            return result is True
            
        except Exception as e:
            logger.error(f"设置缓存失败 {key}: {e}")
            return False
    
    async def get(self, key: str, default: Any = None) -> Any:
        """
        获取缓存值
        
        Args:
            key: 键名
            default: 默认值
            
        Returns:
            Any: 缓存值或默认值
        """
        try:
            if not self._redis:
                return default
            
            value = await self._redis.get(self._get_key(key))
            
            if value is None:
                return default
            
            # 尝试反序列化JSON
            try:
                return json.loads(value)
            except (json.JSONDecodeError, TypeError):
                return value
                
        except Exception as e:
            logger.error(f"获取缓存失败 {key}: {e}")
            return default
    
    async def delete(self, key: str) -> bool:
        """
        删除缓存
        
        Args:
            key: 键名
            
        Returns:
            bool: 删除是否成功
        """
        try:
            if not self._redis:
                return False
            
            result = await self._redis.delete(self._get_key(key))
            return result > 0
            
        except Exception as e:
            logger.error(f"删除缓存失败 {key}: {e}")
            return False
    
    async def exists(self, key: str) -> bool:
        """
        检查键是否存在
        
        Args:
            key: 键名
            
        Returns:
            bool: 键是否存在
        """
        try:
            if not self._redis:
                return False
            
            result = await self._redis.exists(self._get_key(key))
            return result > 0
            
        except Exception as e:
            logger.error(f"检查缓存存在性失败 {key}: {e}")
            return False
    
    async def expire(self, key: str, ttl: int) -> bool:
        """
        设置键的过期时间
        
        Args:
            key: 键名
            ttl: 过期时间（秒）
            
        Returns:
            bool: 设置是否成功
        """
        try:
            if not self._redis:
                return False
            
            result = await self._redis.expire(self._get_key(key), ttl)
            return result is True
            
        except Exception as e:
            logger.error(f"设置缓存过期时间失败 {key}: {e}")
            return False
    
    async def ttl(self, key: str) -> int:
        """
        获取键的剩余过期时间
        
        Args:
            key: 键名
            
        Returns:
            int: 剩余过期时间（秒），-1表示永不过期，-2表示键不存在
        """
        try:
            if not self._redis:
                return -2
            
            return await self._redis.ttl(self._get_key(key))
            
        except Exception as e:
            logger.error(f"获取缓存过期时间失败 {key}: {e}")
            return -2
    
    async def increment(self, key: str, amount: int = 1) -> Optional[int]:
        """
        递增计数器
        
        Args:
            key: 键名
            amount: 递增量
            
        Returns:
            Optional[int]: 递增后的值
        """
        try:
            if not self._redis:
                return None
            
            return await self._redis.incrby(self._get_key(key), amount)
            
        except Exception as e:
            logger.error(f"递增计数器失败 {key}: {e}")
            return None
    
    async def decrement(self, key: str, amount: int = 1) -> Optional[int]:
        """
        递减计数器
        
        Args:
            key: 键名
            amount: 递减量
            
        Returns:
            Optional[int]: 递减后的值
        """
        try:
            if not self._redis:
                return None
            
            return await self._redis.decrby(self._get_key(key), amount)
            
        except Exception as e:
            logger.error(f"递减计数器失败 {key}: {e}")
            return None
    
    async def clear_pattern(self, pattern: str) -> int:
        """
        清除匹配模式的所有键
        
        Args:
            pattern: 键名模式
            
        Returns:
            int: 删除的键数量
        """
        try:
            if not self._redis:
                return 0
            
            keys = await self._redis.keys(self._get_key(pattern))
            if keys:
                return await self._redis.delete(*keys)
            return 0
            
        except Exception as e:
            logger.error(f"清除缓存模式失败 {pattern}: {e}")
            return 0
    
    @property
    def redis(self) -> Optional[Redis]:
        """获取Redis客户端"""
        return self._redis
    
    @property
    def is_connected(self) -> bool:
        """检查是否已连接"""
        return self._is_connected


# 全局Redis配置实例
redis_config = RedisConfig()


async def init_redis() -> bool:
    """
    初始化Redis连接
    
    Returns:
        bool: 初始化是否成功
    """
    return await redis_config.connect()


async def close_redis():
    """
    关闭Redis连接
    """
    await redis_config.disconnect()


def get_redis() -> Redis:
    """
    获取Redis客户端
    
    Returns:
        Redis: Redis客户端
        
    Raises:
        RuntimeError: Redis未连接
    """
    if not redis_config.redis:
        raise RuntimeError("Redis未连接，请先调用 init_redis()")
    
    return redis_config.redis


async def check_redis_health() -> Dict[str, Any]:
    """
    检查Redis健康状态
    
    Returns:
        Dict: 健康检查结果
    """
    return await redis_config.health_check()


# 缓存操作快捷函数
async def cache_set(key: str, value: Any, ttl: Optional[int] = None) -> bool:
    """设置缓存"""
    return await redis_config.set(key, value, ttl)


async def cache_get(key: str, default: Any = None) -> Any:
    """获取缓存"""
    return await redis_config.get(key, default)


async def cache_delete(key: str) -> bool:
    """删除缓存"""
    return await redis_config.delete(key)


async def cache_exists(key: str) -> bool:
    """检查缓存是否存在"""
    return await redis_config.exists(key)


async def cache_expire(key: str, ttl: int) -> bool:
    """设置缓存过期时间"""
    return await redis_config.expire(key, ttl)


# Redis连接重试装饰器
def retry_on_redis_failure(max_retries: int = 3, delay: float = 1.0):
    """
    Redis连接失败重试装饰器
    
    Args:
        max_retries: 最大重试次数
        delay: 重试延迟（秒）
    """
    def decorator(func):
        async def wrapper(*args, **kwargs):
            last_exception = None
            
            for attempt in range(max_retries + 1):
                try:
                    return await func(*args, **kwargs)
                except (ConnectionError, TimeoutError, RedisError) as e:
                    last_exception = e
                    if attempt < max_retries:
                        logger.warning(f"Redis操作失败，{delay}秒后重试 (第{attempt + 1}次): {e}")
                        await asyncio.sleep(delay)
                        # 尝试重新连接
                        if not redis_config.is_connected:
                            await redis_config.connect()
                    else:
                        logger.error(f"Redis操作失败，已达到最大重试次数: {e}")
                        break
                except Exception as e:
                    # 非连接相关错误直接抛出
                    raise e
            
            # 如果所有重试都失败，抛出最后一个异常
            if last_exception:
                raise last_exception
        
        return wrapper
    return decorator